package dao;

import java.io.Serializable;
import java.util.List;
import javax.persistence.EntityExistsException;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;

public abstract class DAO <T> implements Serializable {
    private EntityManagerFactory emf;

    public DAO(EntityManagerFactory emf) {
        this.emf = emf;
    }
    
    public EntityManager getEntityManager() {
        return emf.createEntityManager();
    }
    
    //Must return cq.from(T.class)
    protected abstract Root from(CriteriaQuery cq);
    protected abstract boolean exists(T object);
    
    public void create(T object) throws EntityExistsException {
        if (!exists(object)) {
            EntityManager em = null;
            try {
                em = getEntityManager();
                em.getTransaction().begin();
                em.persist(object);
                em.getTransaction().commit();
            } finally {
                if (em != null) {
                    em.close();
                }
            }
        } else {
            throw new EntityExistsException();
        }
    }
    
    public void create(List<T> objects) throws EntityExistsException {
        EntityManager em = null;
        try {
            em = getEntityManager();
            for (T t : objects) {
                if (!exists(t)) {
                    em.getTransaction().begin();
                    em.persist(t);
                    em.getTransaction().commit();
                } else {
                    throw new EntityExistsException("This appointment already exists.");
                }
            }
        } finally {
            if (em != null) {
                em.close();
            }
        }
    }
    
    public void edit(T object) throws Exception {
        EntityManager em = null;
        try {
            em = getEntityManager();
            em.getTransaction().begin();
            em.merge(object);
            em.getTransaction().commit();
        } finally {
            if (em != null) {
                em.close();
            }
        }
    }
    
    public void destroy(T object) throws Exception {
        EntityManager em = null;
        try {
            em = getEntityManager();
            em.getTransaction().begin();
            T toBeRemoved = em.merge(object);
            em.remove(toBeRemoved);
            em.getTransaction().commit();
        } finally {
            if (em != null) {
                em.close();
            }
        }
    }
    
    public List<T> findEntities() {
        return findEntities(true, -1, -1);
    }
    
    public List<T> findEntities(int maxResults, int firstResult) {
        return findEntities(false, maxResults, firstResult);
    }

    public List<T> findEntities(boolean all, int maxResults, int firstResult) {
        EntityManager em = null;
        try {
            em = getEntityManager();
            CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
            cq.select(from(cq));
            Query q = em.createQuery(cq);
            if (!all) {
                q.setMaxResults(maxResults);
                q.setFirstResult(firstResult);
            }
            return q.getResultList();
        } finally {
            if (em != null) {
                em.close();
            }
        }
    }

}
